package com.itheima.dianming.controller;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.model.OSSObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.dianming.aop.HasAuth;
import com.itheima.dianming.filter.UserHolder;
import com.itheima.dianming.pojo.domain.Exam;
import com.itheima.dianming.pojo.domain.ExamLog;
import com.itheima.dianming.pojo.domain.SignLog;
import com.itheima.dianming.pojo.domain.Stu;
import com.itheima.dianming.pojo.vo.ExamLogVo;
import com.itheima.dianming.service.ExamLogService;
import com.itheima.dianming.service.ExamService;
import com.itheima.dianming.service.SignLogService;
import com.itheima.dianming.service.StuService;
import com.itheima.dianming.template.AliyunOssTemplate;
import com.itheima.dianming.util.ExcelToTable;
import com.itheima.dianming.pojo.vo.R;
import com.itheima.dianming.util.MarkdownToHtmlUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/user")
@RefreshScope
@Slf4j
public class CallController {

    @Value("${call.filePath}")
    private String filePath;

    @Value("${call.am}")
    private String amStr;

    @Value("${call.pm}")
    private String pmStr;

    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private SignLogService signLogService;

    @Autowired
    private StuService stuService;

    @Autowired
    private ExamLogService examLogService;

    @Autowired
    private ExamService examService;

    @Autowired
    private Snowflake snowflake;

    @Autowired
    private AliyunOssTemplate ossTemplate;


    @GetMapping("/dload")
    public void dload(Integer id, HttpServletResponse response,HttpServletRequest request) throws IOException {
        ExamLog examLog = examLogService.getById(id);
        if(examLog!=null){
            String fileId = examLog.getFileUrl()
                    .substring(examLog.getFileUrl().lastIndexOf("/")+1);
            OSSObject download = ossTemplate.download(fileId);
            InputStream objectContent = download.getObjectContent();
            Long stuId = examLog.getStuId();
            Stu stu = stuService.getById(stuId);
            String username = stu.getUsername();
            String title = examLog.getContent();
            String fileName =  username+"-"+title+".md";

            if (request.getHeader("User-Agent").toLowerCase().indexOf("firefox") > 0) {
                fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1"); // firefox浏览器

            } else if (request.getHeader("User-Agent").toUpperCase().indexOf("MSIE") > 0) {
                fileName = URLEncoder.encode(fileName, "UTF-8");// IE浏览器

            }else if (request.getHeader("User-Agent").toUpperCase().indexOf("CHROME") > 0) {
                fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");// 谷歌

            }else {
                fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");// 谷歌
            }
            response.setHeader("Content-Disposition", "attachment; filename="+fileName);
            IoUtil.copy(objectContent,response.getOutputStream());
        }
    }

    @GetMapping("/review")
    public void review(Integer id, HttpServletResponse response) throws IOException {
        ExamLog examLog = examLogService.getById(id);
        if(examLog!=null){
            String fileId = examLog.getFileUrl()
                    .substring(examLog.getFileUrl().lastIndexOf("/")+1);
            OSSObject download = ossTemplate.download(fileId);
            InputStream objectContent = download.getObjectContent();
            String content = StreamUtils.copyToString(objectContent, Charset.forName("UTF-8"));
            log.info(content);
            String html = MarkdownToHtmlUtils.markdownToHtmlExtensions(content);
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(html);
        }
    }


    @DeleteMapping("/delJob")
    public R delJob(Integer id){
        ExamLog byId = examLogService.getById(id);
        if(byId!=null){
            Long stuId = byId.getStuId();
            Stu stu = stuService.getById(stuId);
            if(stu!=null){
                String username = stu.getUsername();
                String loginName = UserHolder.getStu().getUsername();
                if(!"admin".equals(loginName) && !loginName.equals(username)){
                    return R.onflag(false,"非本人，不允许操作！");
                }
            }
        }
        boolean b = examLogService.removeById(id);
        return R.onflag(b);
    }

    @GetMapping("/getjob")
    public R getJob(){
        Exam exam = (Exam) redisTemplate.opsForValue().get("job");
        return R.data(exam);
    }

    /**
     * 列出知识作业列表
     * @return
     */
    @GetMapping("/exams")
    public R exams(){
        LambdaQueryWrapper<Exam> qw = new LambdaQueryWrapper<>();
        qw.eq(Exam::getType,2);
        qw.orderByDesc(Exam::getStartTime);
        List<Exam> list = examService.list(qw);
        return R.data(list);
    }

    /**
     * 列出学生的知识文档列表
     * @param examId
     * @return
     */
    @PostMapping("/listExam")
    public R listExam(String examId){
        if(StrUtil.isBlank(examId) || "null".equalsIgnoreCase(examId)
                || "undefined".equalsIgnoreCase(examId)){
            Exam exam = (Exam) redisTemplate.opsForValue().get("job");
            if(exam!=null){
                examId = exam.getId().toString();
            }else {
                return R.onflag(false);
            }
        }
        List<ExamLogVo> list = examLogService.listExam(examId);
        return R.data(list);
    }

    /**
     * 统计完成的人数
     * @param examId 课堂练习题的id
     * @return R
     */
    @GetMapping("/count")
    public R count(Long examId){
        LambdaQueryWrapper<ExamLog> qw = new LambdaQueryWrapper<>();
        qw.eq(ExamLog::getExamId,examId);
        Long count = examLogService.count(qw);
        return R.data(count);
    }

    /**
     * 检查课堂练习是否结束
     * @return R
     */
    @GetMapping("/isdown")
    public R isdown(){
        Map<String,Object> result = new HashMap();
        result.put("isover",false);
        Exam exam = (Exam) redisTemplate.opsForValue().get("job");
        result.put("exam",exam);
        if(exam==null){
            result.put("isover",true);
            return R.data(result);
        }
        long id = exam.getId();
        LambdaQueryWrapper<ExamLog> qw = new LambdaQueryWrapper<>();
        qw.eq(ExamLog::getExamId,id);
        qw.eq(ExamLog::getStuId,UserHolder.getStuId());
        ExamLog examLog = examLogService.getOne(qw);
        if(examLog!=null){
            result.put("isover",true);
        }
        return R.data(result);
    }

    /**
     * 学生完成课堂练习并提交
     * @param request 请求对象
     * @return R
     */
    @PostMapping("/finishExam")
    public R finishExam(HttpServletRequest request,String fileUrl){
        Exam exam = (Exam) redisTemplate.opsForValue().get("job");
        ExamLog examLog = new ExamLog();
        examLog.setExamId(exam.getId());
        if(exam.getType()==1){
            examLog.setContent(exam.getContent());
        }else {
            examLog.setContent(exam.getContent());
            examLog.setFileUrl(fileUrl);
        }
        examLog.setType(exam.getType());
        examLog.setFinishTime(new Date());
        examLog.setIpaddr(this.getRemoteHost(request));
        examLog.setStartTime(exam.getStartTime());
        examLog.setStuId(UserHolder.getStuId());
        boolean save = examLogService.save(examLog);
        return R.onflag(save);
    }

    /**
     * 发布课堂练习
     * @param exam 课堂练习对象
     * @return R
     */
    @PostMapping("/publishExam")
    @HasAuth("ROLE_ADMIN")
    public R publishJob(@RequestBody Exam exam){
        exam.setStartTime(new Date());
        if(exam.getContent().contains(".")){
            exam.setContent(exam.getContent().substring(0,exam.getContent().indexOf(".")));
        }
        examService.save(exam);
        redisTemplate.opsForValue().set("job",exam,exam.getConstTime()==null?120:exam.getConstTime()+10, TimeUnit.MINUTES);
        return R.all(true,exam.getId());
    }


    /**
     * 读取excel表格转为table标签
     * @return R
     */
    @GetMapping("/initTable")
    @Cacheable(cacheNames = "tableCache",key = "'all'")
    public R initTable()  {
        ExcelToTable excelToTable = null;
        try {
            excelToTable = new ExcelToTable(filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String s = excelToTable.toTable();

        return R.data(s);
    }


    /**
     * 查询所有用户的今日签到记录
     * @return R
     */
    @GetMapping("/all")
    public R all(){
        Map<String,Object> result = new HashMap<>();
        String now = this.getNow();
        List<Map<String,Object>> maps = signLogService.listAll(now);
        for (Map<String,Object> map : maps) {
            result.put((String) map.get("name"),map.get("num"));
        }
        return R.data(result);
    }


    /**
     * 签到
     * @param name 姓名
     * @param request 请求对象
     * @return R
     */
    @GetMapping("/call")
    public R call(String name, HttpServletRequest request) throws ParseException {
        Map result = new HashMap();
        if(name.contains(" ")){
            name = name.substring(0,name.indexOf(" "));
        }
        String nowStr = getNow();
        SimpleDateFormat simpleDateFormat =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long now = new Date().getTime();
        long am = simpleDateFormat.parse(nowStr + " "+amStr).getTime();
        long pm = simpleDateFormat.parse(nowStr + " "+pmStr).getTime();
        User login =
                (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        LambdaQueryWrapper<Stu> qw = new LambdaQueryWrapper<>();
        qw.eq(Stu::getNumber,login.getUsername());
        Stu stu = stuService.getOne(qw);
        if(now<=am){//上午签到
            Map sign = this.sign(request, stu, nowStr, 1);
            return R.data(sign);
        }

        if(now>=pm){//下午签出
            Map sign = this.sign(request, stu, nowStr, 2);
            return R.data(sign);
        }
        //判断签到类型
        result.put("sign",4);
        result.put("msg","非法签到时间");
        // 1签到成功 2请勿重复签到 3非本人签到！！ 4非法签到时间
        return R.data(request);
    }




    private Map sign(HttpServletRequest request,Stu stu,String nowStr,Integer signType){
        Map result = new HashMap();
        LambdaQueryWrapper<SignLog> signqw = new LambdaQueryWrapper<>();
        signqw.eq(SignLog::getIpaddr,this.getRemoteHost(request));
        signqw.eq(SignLog::getSignType,signType);
        signqw.eq(SignLog::getSignDate,nowStr);
        signqw.ne(SignLog::getStuId,stu.getId());
        log.info("{}于{}签到，ip：{}",stu.getId(),nowStr,this.getRemoteHost(request));
        SignLog signLog = signLogService.getOne(signqw);
        if(signLog!=null){
            result.put("sign",3);
            Long stuId = signLog.getStuId();
            Stu stu1 = stuService.getById(stuId);
            result.put("msg",this.getRemoteHost(request)+"已被"+stu1.getUsername()+"占用！！");
            return result;
        }

        signqw = new LambdaQueryWrapper<>();
        signqw.eq(SignLog::getSignDate,nowStr);
        signqw.eq(SignLog::getSignType,signType);
        signqw.eq(SignLog::getStuId,stu.getId());
        signLog = signLogService.getOne(signqw);
        if(signLog==null){
            signLog = new SignLog();
            signLog.setIpaddr(this.getRemoteHost(request));
            signLog.setSignDate(new Date());
            signLog.setSignType(signType);//签到
            signLog.setStuId(stu.getId());
            signLogService.save(signLog);
            result.put("sign",1);
            result.put("msg","签到成功");
        }else {
            result.put("sign",2);
            result.put("msg","请勿重复签到");
        }
        return result;
    }

    private String getNow(){
        return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    }

    private String getRemoteHost(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }
}
